<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>声音控制</title>
    <style>
        *{
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        #types{
            width: 100vw;
            height: 100vh;
            display: flex;
        }
        .fa{
            border: 1px solid #000;
            height: 100%;
            width: calc(100%/7);
            position: relative;
        }
        .top{
            position: absolute;
            width: 50%;
            height: 50vh;
            background-color: #000;
            right: 0;
        }
        .ccc{
            background-color: #ccc;
        }
        #top{
            width: 20vw;
            height: 8vh;
            line-height: 8vh;
            position: fixed;
            top: -5vh;
            display: flex;
            text-align: center;
            left: 50%;
            z-index: 2;
            transform: translateX(-50%);
        }
        #top:hover{
            top: 0vh;
            cursor: pointer;
        }
        #left{
            width: 10vw;
            color: #fff;
            border-radius: 5vh 0 0 5vh;
            color: #000;
            /* background-color: red; */
        }
        #right{
            width: 10vw;
            background-color: #000;
            color: #fff;
            border-radius: 0 5vh 5vh 0;
        }
        .red{
            background-color: red;
        }
        .green{
            background-color: green;
        }
        .fff{
            background-color: #fff;
        }
    </style>
</head>

<body>
    <div id="top">
        <div id="left" onclick="switchtype()">暂停</div>
        <div id="right" onclick="download()">下载</div>
    </div>
    <div id="types">
        <div class="fa" onclick="getIndex(1)">
            <div class="top" onclick="getFaIndex(7,true)"></div>
        </div>
        <div class="fa" onclick="getIndex(2)">
            <div class="top" onclick="getFaIndex(8,true)"></div>
        </div>
        <div class="fa" onclick="getIndex(3)">
            <div class="top" onclick="getFaIndex(9,true)"></div>
        </div>
        <div class="fa" onclick="getIndex(4)">
            <div class="top" onclick="getFaIndex(10,true)"></div>
        </div>
        <div class="fa" onclick="getIndex(5)">
            <div class="top" onclick="getFaIndex(11,true)"></div>
        </div>
        <div class="fa" onclick="getIndex(6)">
            <div class="top" onclick="getFaIndex(12,true)"></div>
        </div>
        <div class="fa" onclick="getIndex(7)">
            <div class="top" onclick="getFaIndex(13,true)"></div>
        </div>
    </div>
</body>
<script>
    const audio_data_list = [261.63, 293.66, 329.63, 349.23, 392.00, 440.00, 493.88].map(v => v)
    const audio_data_listfa = [523, 578, 659, 698,784, 880, 988].map(v => v)
    const audio_type_list = ['triangle'];
    let isPlay = 0;
    let timmer;
    var faDom = document.getElementsByClassName("fa")
    var topDom = document.getElementsByClassName("top")
    var left = document.getElementById("left")
    var right = document.getElementById("right")
    var startTime,endTime;
    var saveList = [];
    var playIndex,savelength=0;
    var typeFlag = false;
    window.onload=()=>{
        typeFlag = true;
        savelength = 0;
        left.innerText="录制"
        saveList = [];
        left.classList.remove("green")
        startTime = new Date().getTime();
        left.classList.remove("red")
        left.classList.add("fff")
    }
    function switchtype(){
        if (!typeFlag) {
            typeFlag = true;
            savelength = 0;
            left.innerText="录制"
            saveList = [];
            left.classList.remove("green")
            startTime = new Date().getTime();
            left.classList.remove("red")
            left.classList.add("fff")
        }else{
            typeFlag = false;
            playIndex = 0;
            savelength=saveList.length;
            left.innerText="播放"
            playOnSave(saveList)
            left.classList.remove("red")
            left.classList.remove("fff")
            left.classList.add("green")
        }
    }
    var obj ={
        a:1,
        s:2,
        d:3,
        f:4,
        g:5,
        h:6,
        j:7,
    }
    var obj2 ={
        q:8,
        w:9,
        e:10,
        r:11,
        t:12,
        y:13,
        u:14,
    }
    function getFaIndex(index,flag){
        if (flag) {
            event.stopPropagation();
        }
        var indexNum = index-7;
        let dom = topDom[indexNum]
        if (!dom) {
           return 
        }
        for (let index = 0; index < topDom.length; index++) {
            const element = topDom[index];
            element.classList.remove("ccc")
        }
        dom.classList.add("ccc")
        play(index,0)
        setTimeout(() => {
            if (dom) {
                    dom.classList.remove("ccc")
                }
        }, 1000);
    }

    function getIndex(index){
        index = index -1;
        let dom = faDom[index]
        if (!dom) {
           return 
        }
        for (let index = 0; index < faDom.length; index++) {
            const element = faDom[index];
            element.classList.remove("ccc")
        }

        dom.classList.add("ccc")
        play(index,1)
        setTimeout(() => {
            if (dom) {
                    dom.classList.remove("ccc")
                }
        }, 1000);
    }
    let timeout = null;
    function debounce (fn, await) {
        let timeout = null;
        //let _that=this
        return function (...args) {
            if (timeout) {
                clearTimeout(timeout);
            }
            timeout = setTimeout(() => {
                fn.apply(this, args)
            }, await)
        }
    }
    function keydown(params) {
        var key = params;
        let index,faindex;
        if (obj[key]) {
            index = obj[key]-1;
        }else{
            faindex = obj2[key];
        }
        if (isNaN(index)) {
            dom = topDom[faindex-8];
            if (!dom) {
                return 
            }
            play(faindex,0)
            for (let index = 0; index < topDom.length; index++) {
                const element = topDom[index];
                element.classList.remove("ccc")
            }
            dom.classList.add("ccc")
            setTimeout(() => {
                if (dom) {
                    dom.classList.remove("ccc")
                }
            }, 1000);
        }else{
            dom = faDom[index]
            if (!dom) {
           return 
        }
            play(index,1)
            for (let index = 0; index < faDom.length; index++) {
                const element = faDom[index];
                element.classList.remove("ccc")
            }
            dom.classList.add("ccc")
            setTimeout(() => {
                if (dom) {
                    dom.classList.remove("ccc")
                }
            }, 1000);
        }
        if (!index||index!=0||!faindex||faindex!=0) {
            return
        }
        for (let index = 0; index < faDom.length; index++) {
            const element = faDom[index];
            element.classList.remove("ccc")
        }
        dom.classList.add("ccc")
    }
    function keydownval(event){
        let value = event.key;
        //获取到用户输入的值进行发送请求操作或者做其他事件操作
        keydown(value);
    }
    keydownval = debounce(keydownval, 500)
    document.onkeydown = keydownval;
    function play(index,type){
        if (typeFlag) {
            var obj = {}
            var time = new Date().getTime() - startTime;
            time = Math.ceil(time*1 *0.01) *100;
            obj.time = time;
            obj.value = index;
            if (saveList.length>=20) {
                saveList = [];
            }
            saveList.push(obj)
        }
        // 创建音频上下文  
        const audioCtx = new AudioContext();
        // 创建音调控制对象  
        const oscillator = audioCtx.createOscillator();
        // 创建音量控制对象  
        const gainNode = audioCtx.createGain();
        // 音调音量关联  
        oscillator.connect(gainNode);
        // 音量和设备关联  
        gainNode.connect(audioCtx.destination);
        oscillator.type = audio_type_list[0];
        if (type) {
            // 音调类型指定为正弦波  
            // 设置音调频率  
            if (!audio_data_listfa[index]) {
                return
            }
            oscillator.frequency.value = audio_data_list[index];
        }else{
                // 音调类型指定为正弦波  
            index = index - 7;
            // 设置音调频率  
            if (!audio_data_listfa[index]) {
                return
            }
            oscillator.frequency.value = audio_data_listfa[index];
        }
        // 先把当前音量设为0  
        gainNode.gain.setValueAtTime(0, audioCtx.currentTime);
        // 0.01秒时间内音量从刚刚的0变成1，线性变化 
        gainNode.gain.linearRampToValueAtTime(1, audioCtx.currentTime + 0.01);
        // 声音走起 
        oscillator.start(audioCtx.currentTime);
        // 1秒时间内音量从刚刚的1变成0.001，指数变化 
        gainNode.gain.exponentialRampToValueAtTime(0.1, audioCtx.currentTime + 1);
        // 1秒后停止声音 
        oscillator.stop(audioCtx.currentTime + 1);
    }
    function playOnSave(saveList){
        console.log(numberToFrequency(saveList));
        var data = numberToFrequency(saveList);
        generateAudioBuffer()
        // var res = generateAudioBuffer(data,100);
        // console.log(res);
        let i = 0;
        timmer = setInterval(() => {
            i = i+100;
            var item = saveList.find(item=>i==item.time);
            if (typeof item == "object") {
                playIndex++;
                let value = item.value;
                if (value<7) {
                    value++;
                    getIndex(value)
                }else{
                    getFaIndex(value,false)
                }
            }
            if (playIndex>=savelength) {
                clearInterval(timmer);
            }
        }, 100);
    }
    function numberToFrequency(saveList){
        // console.log(saveList);
        return saveList.map((item)=>{
            if (item.value<7) {
                return{
                    time:item.time,
                    value:audio_data_list[item.value]
                }
            }
            else{
                return{
                    time:item.time,
                    value:audio_data_listfa[item.value-7]
                }
            }
        })
    }
    let generateAudioBuffer = () => {
        const audioCtx = new (window.AudioContext || window.webkitAudioContext)();

        // Create an empty three-second stereo buffer at the sample rate of the AudioContext
        const myArrayBuffer = audioCtx.createBuffer(
            2,
            audioCtx.sampleRate * 3,
            audioCtx.sampleRate
        );

        // Fill the buffer with white noise;
        // just random values between -1.0 and 1.0
        for (let channel = 0; channel < myArrayBuffer.numberOfChannels; channel++) {
        // This gives us the actual ArrayBuffer that contains the data
        const nowBuffering = myArrayBuffer.getChannelData(channel);
        for (let i = 0; i < myArrayBuffer.length; i++) {
            // Math.random() is in [0; 1.0]
            // audio needs to be in [-1.0; 1.0]
            nowBuffering[i] = Math.random() * 2 - 1;
        }
        }

        // Get an AudioBufferSourceNode.
        // This is the AudioNode to use when we want to play an AudioBuffer
        const source = audioCtx.createBufferSource();
        // set the buffer in the AudioBufferSourceNode
        source.buffer = myArrayBuffer;
        // connect the AudioBufferSourceNode to the
        // destination so we can hear the sound
        source.connect(audioCtx.destination);
        // start the source playing
        source.start();
        source.download()
    }
    let getArrayMax = (arr) => {
        let max = arr[0]
        arr.forEach((item, index) => {
            max = max > item ? max : item
        })
        return max
    }
    let getArrayMin = (arr) => {
        let min = arr[0]
        arr.forEach((item, index) => {
            min = min < item ? min : item
        })
        return min
    }
</script>

</html>